/**
 * $Id: CMPP30MessageConvertor.java,v 1.51 2006/09/22 07:57:09 martin Exp $
 *
 * Copyright 2006 Skyinfo, Inc. All Rights Reserved.
 */
package com.haoxi.gateway.messageconvertor.impl;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.haoxi.gateway.cmpp30msg.CMPPCommon;
import com.haoxi.gateway.cmpp30msg.CMPPDeliver;
import com.haoxi.gateway.cmpp30msg.CMPPDeliverRep;
import com.haoxi.gateway.cmpp30msg.CMPPStatusRpt;
import com.haoxi.gateway.cmpp30msg.CMPPSubmit;
import com.haoxi.gateway.cmpp30msg.CMPPSubmitRep;
import com.haoxi.gateway.common.CommonMsgBean;
import com.haoxi.gateway.common.INFOConst;
import com.haoxi.gateway.common.Text;
import com.haoxi.gateway.messageconvertor.MessageConvertor;
import com.haoxi.gateway.runner.RunnerMgr;
import com.haoxi.gateway.runner.smscommand.BaseCommandObj;
import com.haoxi.gateway.runner.smscommand.CommandMOResp;
import com.haoxi.gateway.runner.smscommand.CommandReportResp;
import com.haoxi.gateway.runner.smscommand.SubmitCmd;
import com.haoxi.gateway.runner.smscommand.SubmitReportCmd;
import com.haoxi.gateway.runner.smscommand.SubmitStatusCmd;
import com.haoxi.gateway.runner.smscommand.UserMOCmd;

/**
 * 参照CMPP3.0协议中定义的消息体结构，实现的消息转换器
 * 
 * Revision History
 * 
 * 2006-5-25,star.Zhao,created it
 */
public class CMPP30MessageConvertor extends MessageConvertor {
    private static final Log log = LogFactory
            .getLog(CMPP30MessageConvertor.class);
    private String protcolVersion;
    private static final byte MSG_LEVEL = 3;
    private static final byte FEE_TYPE = 3;

    private static final char[] SPECIAL_CHAR = { '\'' };

    /**
     * 构造函数
     */
    public CMPP30MessageConvertor(String gatewayCode) {
        super(gatewayCode);
        this.protcolVersion = INFOConst.CMPP30_VERSION;
    }

    // /**
    // * 消息命令
    // */
    // private int commid = 0;
    //
    // /**
    // * 命令长度
    // */
    //
    // private int commandLength = 0;
    // /**
    // * 保留字段,stat有用
    // */
    //
    // private Object reserve;
    // /**
    // * 消息的序列号
    // */
    //
    // private int sequence_ID;

    /**
     * 根据BaseCommandObj构造网关需要的消息
     * 
     * @param BaseCommandObj obj
     * @return CommonMsgBean 见CommonMsgBean定义
     * @author martin
     */
    public CommonMsgBean convertCMDToCommonMsg(BaseCommandObj obj) {
        int messageLength = obj.getCommandLength();
        byte[] body = new byte[messageLength];
        CommonMsgBean commonMsgBean = new CommonMsgBean();
        // commonMsgBean.setReserve(obj.getReserved());
        commonMsgBean.setGateWayCode(gatewayCode);
        try {
            // 只有deliverRep
            if ((obj instanceof CommandMOResp)) {
                CommandMOResp commResp = (CommandMOResp) obj;
                int result = Integer.parseInt(commResp.getStatus());
                int sequenceID = Integer.parseInt(commResp.getSequence());
                CMPPDeliverRep deliverRep = new CMPPDeliverRep(sequenceID,
                        commResp.getMsgId_bytes(), result);
                body = deliverRep.getBytes();
                commonMsgBean.setBody(body);
                commonMsgBean.setReserve(commResp);
                commonMsgBean.setConnName(commResp.getConnName()); // 记录连接名

            } else if ((obj instanceof CommandReportResp)) {
                CommandReportResp commResp = (CommandReportResp) obj;
                int result = Integer.parseInt(commResp.getStatus());
                int sequenceID = Integer.parseInt(commResp.getSequence());
                CMPPDeliverRep deliverRep = new CMPPDeliverRep(sequenceID,
                        commResp.getMsgId_bytes(), result);
                body = deliverRep.getBytes();
                commonMsgBean.setBody(body);
                commonMsgBean.setReserve(commResp);
                commonMsgBean.setConnName(commResp.getConnName()); // 记录连接名
            } else if (obj instanceof SubmitCmd) {

                SubmitCmd subCmd = (SubmitCmd) obj;
                // 消息流水号,顺序累加,步长为1,循环使用（一对请求和应答消息的流水号必须相同）
                int sequenceID = getSequence();

                // 信息标识，由SP接入的短信网关本身产生，本处填空。
                byte[] msg_Id = new byte[8];
                // 相同Msg_Id的信息总条数
                int pk_total = 1;
                // 相同Msg_Id的信息序号
                int pk_number = 1;
                // 是否要求返回状态确认报告：0：不需要1：需要
                int registered_Delivery = subCmd.getReg_del();
                // 业务标识，是数字、字母和符号的组合。
                String service_id = subCmd.getServiceId();
                // 计费用户类型字段
                // 0：对目的终端MSISDN计费；
                // 1：对源终端MSISDN计费；
                // 2：对SP计费;
                // 3：表示本字段无效，对谁计费参见Fee_terminal_Id字段。
                int fee_UserType = FEE_TYPE;
                // 被计费用户的号码（如本字节填空，则表示本字段无效，对谁计费参见Fee_UserType字段，本字段与Fee_UserType字段互斥）
                String fee_terminal_id = subCmd.getFeeMobile();
                // 被计费用户的号码类型，0：真实号码；1：伪码
                int fee_terminal_type = subCmd.getFeeMobileType();
                // GSM协议类型
                int tP_pid = subCmd.getTpPId();
                // GSM协议类型
                int tP_udhi = subCmd.getTpUdhi();
                // 信息格式
                // 0：ASCII串
                // 3：短信写卡操作
                // 4：二进制信息
                // 8：UCS2编码
                // 15：含GB汉字
                int msg_fmt = subCmd.getMsgCoding();
                // 信息内容来源(SP_Id)
                String msg_src = RunnerMgr.getInstance().getGatewayInfo()
                        .getIcp_id();

                // 资费类别
                // 01：对“计费用户号码”免费
                // 02：对“计费用户号码”按条计信息费
                // 03：对“计费用户号码”按包月收取信息费
                // 04：对“计费用户号码”的信息费封顶
                // 05：对“计费用户号码”的收费是由SP实现
                String feeType = subCmd.getFeeType();
                // 资费代码（以分为单位）
                String feeCode = String.valueOf(subCmd.getFeeCode());
                if (feeCode.length() < 6) {
                    String tmp = "000000";
                    feeCode = tmp.substring(0, 6 - feeCode.length()) + feeCode;
                }
                // 存活有效期，格式遵循SMPP3.3协议
                String valid_time = subCmd.getExpireTime();
                // 定时发送时间，格式遵循SMPP3.3协议
                String at_time = subCmd.getScheduleTime();
                // 源号码
                // SP的服务代码或前缀为服务代码的长号码,
                // 网关将该号码完整的填到SMPP协议Submit_SM消息相应的source_addr字段，
                // 该号码最终在用户手机上显示为短消息的主叫号码
                String src_terminal_id = subCmd.getOrgMobile();
                // 接收信息的用户数量(小于100个用户)
                int user_number = 1;
                // 接收短信的MSISDN号码
                String[] phone_num = new String[] { subCmd.getDestMobile() };
                // 接收短信的用户的号码类型，0：真实号码；1：伪码
                int dest_terminal_type = subCmd.getDestMobileType();
                // 信息内容
                int stype = subCmd.getStype();
                String content = subCmd.getMsgContent();
                // int msgbyteslen=content.getBytes().length;
                // if(msgbyteslen>140)
                // {
                // content=cut(content,140);
                // }

                byte[] mes = null;
                if (stype == 200 || stype == 400) {
                    String strPos = "|~@$%^&~|";
                    String url = "";
                    String name = "";
                    int pos = content.indexOf(strPos);
                    url = content.substring(0, pos);
                    name = content.substring(pos + 9);
                    log.debug("push url:" + url);
                    log.debug("push name:" + name);
                    String pdu = getPushPdu(url, name);
                    mes = convertStringToBytes(pdu);
                } else {
                    mes = Text.textToByte(msg_fmt, content);
                }
                // 点播业务使用的LinkID，非点播类业务的MT流程不使用该字段
                String linkID = subCmd.getLinkID();

                // 设置转换的Bean的Reserve
                subCmd.setSequence("" + sequenceID);
                // 对content的中特殊字符做特殊转义处理.
                subCmd.setMsgContent(convertSpecialChar(content));
                commonMsgBean.setReserve(subCmd);
                // 构造submit消息
                CMPPSubmit submitMsg = new CMPPSubmit(sequenceID, msg_Id,
                        pk_total, pk_number, registered_Delivery, MSG_LEVEL,
                        service_id, fee_UserType, fee_terminal_id,
                        fee_terminal_type, tP_pid, tP_udhi, msg_fmt, msg_src,
                        feeType, feeCode, valid_time, at_time, src_terminal_id,
                        user_number, phone_num, dest_terminal_type, mes, linkID);
                // 得到消息内容
                body = submitMsg.getBytes();
                // 将消息内容封装到CommonMsgBean中
                commonMsgBean.setBody(body);
            }
            // 暂时不处理异常
        } catch (Exception ex) {
            if (null != obj) {
                log.error("baseObj : " + obj.toString());
            } else {
                log.error("baseObj is null !!!");
            }
            log
                    .error(
                            "Exception in CMPP30MessageConvertor.convertCMDToCommonMsg(): Exception",
                            ex);
            commonMsgBean = null;
        }
        return commonMsgBean;
    }

    /**
     * 把接收到user Mo byte数组中数据转换为UserMOCmd 解析deliver消息
     * 
     * @param msgBean
     * @return
     */
    private UserMOCmd convertToUserMOCmd(int commid, int commandLength,
            int sequence_ID, Object reserve, CMPPDeliver deliverMsg,
            String connName) {
        UserMOCmd mocmd = new UserMOCmd();
        // 消息命令字
        mocmd.setCommandID(commid);
        // 消息长度
        mocmd.setCommandLength(commandLength);
        // 目标号码
        mocmd.setDestMobile(deliverMsg.getDest_id());
        // 目的号码的类型0--真实号码1--伪码CMPP30协议中未规定，这里直接填0
        mocmd.setDestMobileType(INFOConst.TRUE_TYPE);
        // 网关编码
        mocmd.setGateWayCode(gatewayCode);
        // 点播业务使用的LinkID，非点播类业务的MT流程不使用该字段
        mocmd.setLinkId(deliverMsg.getLinkID());
        // 消息编码
        int msgFormat = deliverMsg.getMsgFormat();
        mocmd.setMsgCoding(msgFormat);
        // 消息内容
        String msgContent = Text.getText(msgFormat, deliverMsg.getContent());
        mocmd.setMsgContent(msgContent);
        // 消息ID
        byte[] msgDdBytes = deliverMsg.getMsgId();
        String msgId = this.getMsgId(msgDdBytes);
        // for (int i = 0; i < msgDdBytes.length; i++) {
        // msgId += msgDdBytes[i];
        // }
        // String msgId =
        // String.valueOf(Common.BytesToInt(deliverMsg.getMsgId()));
        mocmd.setMsgId(msgId);
        // 源终端MSISDN号码（状态报告时填为CMPP_SUBMIT消息的目的终端号码）
        mocmd.setOrgMobile(deliverMsg.getSrcTerminalId());
        // 源终端号码类型，0：真实号码；1：伪码
        mocmd.setOrgMobileType(deliverMsg.getSrc_Terminal_Type());
        // 移动的CMPP30协议中取消了保留字段，填空
        mocmd.setReserved(reserve);
        // 消息序列号
        mocmd.setSequence(String.valueOf(sequence_ID));
        // 业务编号
        mocmd.setServiceId(deliverMsg.getServiceId());
        // 移动CMPP没有SP编号，这里填目的号码
        mocmd.setSpNumber(deliverMsg.getDest_id());
        mocmd.setMsgId_bytes(deliverMsg.getMsgId());
        mocmd.setConnName(connName); // 记录连接
        return mocmd;
    }

    /**
     * 把接收到submit status中 byte数组中数据转换为SubmitStatusCmd
     * 
     * @param submitStatusMsg
     * @return SubmitStatusCmd
     * @author martin
     */
    private SubmitStatusCmd convertToSubmitStatusCmd(int commid,
            int commandLength, int sequence_ID, Object reserve,
            CMPPSubmitRep submitRep) {
        SubmitStatusCmd submitCmd = new SubmitStatusCmd();
        submitCmd.setCommandID(commid);
        submitCmd.setCommandLength(commandLength);
        String msgid = this.getMsgId(submitRep.getMsgIdbytes());
        submitCmd.setMsgID(msgid);
        submitCmd.setReserved(reserve);
        submitCmd.setSequence(String.valueOf(sequence_ID));
        submitCmd.setStatus(String.valueOf(submitRep.getResult()));

        return submitCmd;
    }

    /**
     * 把接收到submit report中 byte数组中数据转换为SubmitReportCmd
     * 
     * @param submitReportMsg
     * @return SubmitReportCmd
     * @author martin
     */
    private SubmitReportCmd convertToSubmitReportCmd(int commid,
            int commandLength, int sequence_ID, Object reserve,
            CMPPStatusRpt status, String connName, byte[] deliver_MsgId_bytes) {
        SubmitReportCmd report = new SubmitReportCmd();
        report.setCommandID(commid);
        report.setCommandLength(commandLength);
        String msgid = this.getMsgId(status.getMsgIdbytes());
        report.setMsgID(msgid);
        report.setReport(status.getStat());
        report.setReserved(reserve);
        report.setSequence(String.valueOf(sequence_ID));
        report.setUserNumber(status.getDest_terminal_Id());
        // 长号码
        report.setUserNumberType(INFOConst.TRUE_TYPE);
        report.setMsgId_bytes(deliver_MsgId_bytes);
        report.setConnName(connName);// 记录连接
        return report;
    }

    /**
     * 转换从网关下发的消息,最终转换成BaseCommandObj
     * 
     * @param CommonMsgBean commonMsgBean 包含了网关下发的消息，网关编号
     * @BaseCommandObj 见BaseCommandObj定义
     * @author martin
     */
    public BaseCommandObj convertCommonMsgToCMD(CommonMsgBean commonMsgBean) {
        BaseCommandObj result = null;
        Object reserve = commonMsgBean.getReserve();
        String connName = commonMsgBean.getConnName(); // 记录连接
        byte[] body = commonMsgBean.getBody();
        /* 消息头 */
        byte tmp[] = new byte[4];
        System.arraycopy(body, 0, tmp, 0, 4);
        // 消息长度
        int commandLength = CMPPCommon.Bytes4ToInt(tmp);
        System.arraycopy(body, 4, tmp, 0, 4);
        // 消息命令
        int commid = CMPPCommon.Bytes4ToInt(tmp);
        System.arraycopy(body, 8, tmp, 0, 4);
        // 消息序列号
        int sequence_ID = CMPPCommon.Bytes4ToInt(tmp);

        /* end消息头 */
        tmp = new byte[commandLength - CMPPCommon.MSG_HEAD_LENGTH];
        System.arraycopy(body, CMPPCommon.MSG_HEAD_LENGTH, tmp, 0, tmp.length);
        if (CMPPCommon.CMPP_DELIVER == commid) {
            CMPPDeliver deliverMsg = new CMPPDeliver(tmp);
            if (deliverMsg.isStatusRpt()) {
                CMPPStatusRpt status = new CMPPStatusRpt(deliverMsg
                        .getContent());
                result = convertToSubmitReportCmd(commid, commandLength,
                        sequence_ID, reserve, status, connName, deliverMsg
                                .getMsgId());
            } else {
                // 处理UserMO消息
                result = convertToUserMOCmd(commid, commandLength, sequence_ID,
                        reserve, deliverMsg, connName);
            }
        } else if (CMPPCommon.CMPP_SUBMIT_REP == commid) {
            try {
                CMPPSubmitRep submitRep = new CMPPSubmitRep(tmp);
                result = convertToSubmitStatusCmd(commid, commandLength,
                        sequence_ID, reserve, submitRep);
            } catch (Exception ex) {
                result = null;
                log
                        .error(
                                "Exception in CMPP30MessageConvertor.convertCommonMsgToCMD(): Exception",
                                ex);
            }
        }
        return result;
    }

    /**
     * @return Returns the protcolVersion.
     */
    public String getProtcolVersion() {
        return protcolVersion;
    }

    public String getPushPdu(String pushUrl, String pushName) {
        StringBuffer pdu = new StringBuffer();
        pdu.append("0605040B8423F0");
        pdu.append("DC0601AE02056A0045C60C03");
        // 将网址转换到UTF8进制
        pdu.append(stringToUTF8(handleUrl(pushUrl)));
        pdu.append("000103");
        // 将网址名字转换到utf8编码
        pdu.append(stringToUTF8(pushName));
        pdu.append("000101");
        return pdu.toString().toUpperCase();
    }

    public String handleUrl(String url) {
        String handleUrl = "";
        String head = "http://";
        try {
            if (url.indexOf(head) != -1) // 如果有http://头部，则除去头部
            {
                handleUrl = url.substring(head.length());
            } else // 返回原值
            {
                handleUrl = url;
            }

            return handleUrl;
        } catch (Exception ex) {

        }

        return handleUrl;
    }

    public String stringToUTF8(String s) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);

            if (c >= 0 && c <= '\377') {
                String hex = Integer.toHexString(c).length() >= 2 ? Integer
                        .toHexString(c) : "0" + Integer.toHexString(c);
                sb.append(hex);
            } else {
                byte b[];
                try {
                    b = Character.toString(c).getBytes("UTF-8");
                } catch (Exception ex) {
                    log.error("", ex);
                    b = new byte[0];
                }
                for (int j = 0; j < b.length; j++) {
                    int k = b[j];

                    if (k < 0) {
                        k += 256;
                    }
                    sb.append(Integer.toHexString(k).toUpperCase());
                }

            }
        }

        return sb.toString().toUpperCase();
    }

    public byte[] convertStringToBytes(String srcBuff) {
        byte dest[] = (byte[]) null;
        try {
            dest = new byte[srcBuff.length() / 2];
            int j = 0;
            for (int i = 0; i < srcBuff.length();) {
                String subBuff = srcBuff.substring(i, i + 2);
                byte ab = (byte) Integer.parseInt(subBuff, 16);
                dest[j] = ab;
                i += 2;
                j++;
            }

            return dest;
        } catch (Exception exception) {
            return dest;
        }
    }

    public static String cut(String str, int bytesCount) {

        byte[] bytes = str.getBytes();
        char[] chars = new String(bytes, 0, bytesCount).toCharArray();
        char[] charsPlus = new String(bytes, 0, bytesCount + 1).toCharArray();

        if (chars.length == charsPlus.length) {
            return new String(bytes, 0, bytesCount - 1);
        }
        return new String(bytes, 0, bytesCount);

    }

    public String convertSpecialChar(String temp) {
        String result = "";
        if (SPECIAL_CHAR.length > 0) {
            for (int i = 0; i < SPECIAL_CHAR.length; i++) {
                int j = temp.indexOf(SPECIAL_CHAR[i]);
                while (-1 != j) {
                    result += temp.substring(0, j) + "/"
                            + Character.toString(SPECIAL_CHAR[i]);
                    temp = temp.substring(j + 1);
                    j = temp.indexOf(SPECIAL_CHAR[i]);
                }
                result += temp;
                temp = result;
            }
        }
        return result;
    }

    private String getMsgId(byte[] bytes) {
        long lontmp = (long) bytes[7] & (long) 255
                | ((long) bytes[6] & (long) 255) << 8
                | ((long) bytes[5] & (long) 255) << 16
                | ((long) bytes[4] & (long) 255) << 24
                | ((long) bytes[3] & (long) 255) << 32
                | ((long) bytes[2] & (long) 255) << 40
                | ((long) bytes[1] & (long) 255) << 48 | (long) bytes[0] << 56;
        String tmp = "";
        tmp = Long.toBinaryString(lontmp);
        // 不足64位前补0操作
        if (tmp.length() < 64) {
            String tmp0 = "000";
            tmp = tmp0.substring(0, 64 - tmp.length()) + tmp;
        }
        StringBuffer temp = new StringBuffer();
        // 对二进制串进行分割后得到最终需要的字符串
        int month = Integer.parseInt(tmp.substring(0, 4), 2);
        int day = Integer.parseInt(tmp.substring(4, 9), 2);
        int hour = Integer.parseInt(tmp.substring(9, 14), 2);
        int min = Integer.parseInt(tmp.substring(14, 20), 2);
        int sec = Integer.parseInt(tmp.substring(20, 26), 2);
        int gatewaycode = Integer.parseInt(tmp.substring(26, 48), 2);
        int seq = Integer.parseInt(tmp.substring(48), 2);
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.MONTH, month - 1);
        calendar.set(Calendar.DATE, day);
        calendar.set(Calendar.HOUR_OF_DAY, hour);
        calendar.set(Calendar.MINUTE, min);
        calendar.set(Calendar.SECOND, sec);
        SimpleDateFormat dateformat = new SimpleDateFormat("MMddHHmmss");
        Date date = calendar.getTime();
        tmp = dateformat.format(date);
        temp.append(tmp).append(gatewaycode).append(seq);
        return temp.toString();
    }

}
